#!perl

###############################################################################
# Copyright (c) 2008 Eugene Kalinin <Jenia.Kalinin@gmail.com>. All rights reserved.
###############################################################################

use strict;

use CGI;
use URI::URL;
use DateTime;
use File::Slurp;
use MIME::Base64;
use Error qw(:try);
use HTML::Template;
use URI::Escape;

#config
my $defaultPage = "syntax";
my $skin = "default";
my $pagesDir = "pages/";
my $templateDir = "tmpl/" . $skin . "/";
my $pagesExt = ".txt";

my $htmlCommentBegin = "<!--";
my $htmlCommentEnd = "-->";
my $localTimezone = "+03:00";

#global vars
my $cgi = CGI->new();
#my $cgi = CGI->new(\*STDIN);

my $wikiContent = "";
my $htmlContent = "";
my $modificationTime = "";

my $error = 0;
my $page = $cgi->param("page");
my $action = $cgi->param("action");

sub readFile($)
{    
    my ($filename, $str) = (shift, "");    
    try
    {    
        my $path = $pagesDir . $filename . $pagesExt;
        $str = read_file( $path );
        #get modification time
        my $fileInfo = (stat($path))[9];
        my $dt = DateTime->now( epoch => $fileInfo, time_zone => $localTimezone );
        $modificationTime = $dt->dmy("/") . " " . substr($dt->hms(), 0, 5);
    }    
    catch Error with
    {
        $error = 1;
    };        
    return $str;
}

sub generatePage($)
{        
    my $header = $cgi->header( -type => 'text/html', -charset=>'utf-8');
    my $tmpl = HTML::Template->new(filename    => $templateDir . shift);    

    if (! $error)
    {
        $tmpl->param(WIKI_CONTENT => $wikiContent);
        $tmpl->param(HTML_CONTENT => $htmlContent);
        
        $tmpl->param(COMMENT_BEGIN => $htmlCommentBegin);
        $tmpl->param(COMMENT_END => $htmlCommentEnd);
    }    
    $tmpl->param(PAGE_TITLE => htmlspecialchars($page));    
    $tmpl->param(URL_PAGE_TITLE => uri_escape($page));    
    
    
    my $dt = DateTime->now(time_zone => $localTimezone);
    $tmpl->param(CURRENT_TIME => ($dt->dmy('/') . " " . $dt->hms(':')));
    $tmpl->param(MODIFICATION_TIME => $modificationTime);
    #other
    $tmpl->param(REMOTE_ADDR => $ENV{REMOTE_ADDR});
    $tmpl->param(TEMPLATE_DIR => $templateDir);
    
    return $header . $tmpl->output();
}

###############################################################################
# Parsing wiki syntax
###############################################################################
sub parseTextStyle($)
{
    my $str = shift;
    
    $str =~ s/'__(.*?)__'/<u>$1<\/u>/gi; #underlining    
    $str =~ s/'''''(.*?)'''''/<strong><em>$1<\/em><\/strong>/gi; # bold and italic
    $str =~ s/'''(.*?)'''/<strong>$1<\/strong>/gi; #bold
    $str =~ s/''(.*?)''/<em>$1<\/em>/gi; #italic    
    $str =~ s/'--(.*?)--'/<del>$1<\/del>/gi; #strikethrough    
    
    $str =~ s/{small}(.*?){\/small}/<small>$1<\/small>/gi; # small
    $str =~ s/{sup}(.*?){\/sup}/<sup>$1<\/sup>/gi; # sup
    $str =~ s/{sub}(.*?){\/sub}/<sub>$1<\/sub>/gi; # sub
    
    $str =~ s/{br}/<br style="clear:both;" \/>/gi; # new line
    $str =~ s/^([^!\*#\r\n](.*))\n/<p>$1<\/p>\r\n/gmi; # new line
    $str =~ s/----/<hr \/>/gi; # horisontal line
    
    #headers   
    # aфигеть - полет мысли о_О
    $str =~ s/^(!{1,4})(.*?)$/'<h'.(5-length($1)).">$2<\/h".(5-length($1)).'>'/gmie;
    
    #special chars
    $str =~ s/&lt;-->/&harr;/gi; #  <-->
    $str =~ s/-->/&rarr;/gi; #  -->
    $str =~ s/&lt;--/&larr;/gi; #  <--    
    $str =~ s/\([cC]\)/&copy;/gi; #  (c)
    $str =~ s/\([rR]\)/&reg;/gi; #  (r)
    $str =~ s/--/&mdash;/gi; #  (r)    
    
    $str =~ s/( +) ([^ ])/$1&nbsp;&nbsp;&nbsp;&nbsp;$2/gi; # space
    
    return $str;
}


sub parseLists($)
{
    my $str = shift;
    
    #ordered unordered list
    $str =~ s/^\*\*\*(.*?)(\n)/<ul><ul><ul><li>$1<\/li><\/ul><\/ul><\/ul>$2/gmi;
    $str =~ s/^\*\*(.*?)(\n)/<ul><ul><li>$1<\/li><\/ul><\/ul>$2/gmi;
    $str =~ s/^\*(.*?)(\n)/<ul><li>$1<\/li><\/ul>$2/gmi;
        
    $str =~ s/^###(.*?)(\n)/<ol><ol><ol><li>$1<\/li><\/ol><\/ol><\/ol>$2/gmi;
    $str =~ s/^##(.*?)(\n)/<ol><ol><li>$1<\/li><\/ol><\/ol>$2/gmi;
    $str =~ s/^#(.*?)(\n)/<ol><li>$1<\/li><\/ol>$2/gmi;
    
    #merge odered list 
    for (my $i = 3; $i > 0; $i--)
    {
        $str =~ s/(<\/ol>(\n)*<ol>|<\/ul>(\n)*<ul>)//gi;
    }    
    
    $str =~ s/<\/li><([uo])l>/<$1l>/gi;
    $str =~ s/<\/([uo])l><li>/<\/$1l><\/li><li>/gi;
    $str =~ s/<(\/?)([uo])l><\/?[uo]l>/<$1$2l><$1li><$1$2l>/gi;
    
    return $str;
}

sub parseContent()
{    
    #init parse
    $htmlContent = $wikiContent;
    
    # internal comments
    $htmlContent =~ s/(=?[^\^])<!--.*?-->//gi;
    
    #escape ^ code    
    $htmlContent =~ s/\^(.)/'&#'.ord($1).';'/gie;    
    
    $htmlContent =~ s/(\r\n|\r)/\n/sgi;    # to unix new line

    $htmlContent =~ s/</&lt;/gi; #  <    
    $htmlContent =~ s/>/&gt;/gi; #  >    
    #$page =~ s/&/&amp;/gi; #  &
        
    $htmlContent = recieveCodeMarkup($htmlContent);
        
    $htmlContent = parseLinks($htmlContent);
    $htmlContent = parseTextStyle($htmlContent);
    $htmlContent = parseLists($htmlContent);    
    
    $htmlContent = insertCodeMarkup($htmlContent);

    
}

my @Code = ();

sub parseLinks($)
{
    my $str = shift;
    
    my $url = "[-0-9a-zA-Z\.\#/_\?\&,\+\:@;!\(\)\*\$']*";
    
    my $httpImageRegExp = "(https?://$url\.(jpeg|jpg|gif|png))";
    my $imageRegExp = "($url\.(jpeg|jpg|gif|png))";    
    my $httpLinkRegExp = "(https?://$url)";
    my $linkRegExp = "($url)";
    
    $str =~ s/\[$httpImageRegExp(\|(right|left))?\]/"<img src=\"$1\" alt=\"$1\" style=\"float:$4;\"\/>"/gmie;
    $str =~ s/\[$imageRegExp(\|(right|left))?\]/"<img src=\"$1\" alt=\"$1\" style=\"float:$4;\"\/>"/gmie;
    
    $str =~ s/\[$httpImageRegExp\|$httpLinkRegExp(\|(right|left))?\]/"<a href=\"$3\" class=\"url\"><img src=\"$1\" alt=\"$3\" title=\"$3\" style=\"float:$5;\" \/><\/a>"/gmie;
    $str =~ s/\[$httpImageRegExp\|$linkRegExp(\|(right|left))?\]/"<a href=\"pearlWiki.pl?page=$3\" class=\"url\"><img src=\"$1\" alt=\"$3\" title=\"$3\" style=\"float:$5;\" \/><\/a>"/gmie;
    $str =~ s/\[$imageRegExp\|$httpLinkRegExp(\|(right|left))?\]/"<a href=\"$3\" class=\"url\"><img src=\"$1\" alt=\"$3\" title=\"$3\" style=\"float:$5;\" \/><\/a>"/gmie;
    $str =~ s/\[$imageRegExp\|$linkRegExp(\|(right|left))?\]/"<a href=\"pearlWiki.pl?page=$3\" class=\"url\"><img src=\"$1\" alt=\"$3\" title=\"$3\" style=\"float:$5;\" \/><\/a>"/gmie;
    
    $str =~ s/([0-9a-zA-Z\.\/\~\-\_]+@[0-9a-z\.\/\~\-\_]+)/<a href="mailto:$1">$1<\/a>/gsi;
    
    $str =~ s/\[([^\]]+)\|$httpLinkRegExp\]/"<a href=\"$2\" class=\"url\">$1<\/a>"/gmie;
    $str =~ s/\[$httpLinkRegExp\]/"<a href=\"$1\" class=\"url\">$1<\/a>"/gmie;
    
    $str =~ s/\[([^\]]+)\|\.\/$linkRegExp\]/"<a href=\"pearlWiki.pl?page=$2\" class=\"url\">$1<\/a>"/gmie;
    
    return $str;
}


sub insertCodeMarkup($)
{
    my $html = shift;    
    while ($#Code >= 0)
    {
        my $value = shift @Code;
        $html =~ s/\^CODE\^/"<pre><code>" . $value . "<\/code><\/pre>"/sie;
    }    
    return $html;
}

sub recieveCodeMarkup($)
{
    my $html = shift;    
    while ($html =~ s/{{(.*?)}}/^CODE^/si)
    {        
        push @Code, $1;        
    }    
    return $html;
}

sub saveWikiMarkup($$)
{
    my ($filename, $str) = (shift, shift);
    $filename = uri_escape($filename);
    open my $file, ">", $pagesDir . $filename . $pagesExt;
    return 1 unless $file;
    print $file $str;
    close $file;
    return 0;
}

sub htmlspecialchars($)
{
    my $str = shift;    
    
    $str =~ s/"/&quot;/gi; #  "
    $str =~ s/'/&#039;/gi; #  '
    $str =~ s/</&lt;/gi; #  <    
    $str =~ s/>/&gt;/gi; #  >    
    
    return $str;
}


sub htmlentities($)
{
    my $str = shift;    
    
    $str =~ s/&quot;/"/gi; #  "
    $str =~ s/&#039;/'/gi; #  '
    $str =~ s/&lt;/</gi; #  <    
    $str =~ s/&gt;/>/gi; #  >    
    
    return $str;
}

sub main(@)
{            
    $page = $defaultPage if ($page eq "");
    
    $wikiContent = $cgi->param("wikiContent");
    if ($wikiContent eq "")
    {        
        $wikiContent = readFile($page);
    }
    
    if ($action eq "edit")
    {        
        #view in text area
        $htmlCommentBegin = "";
        $htmlCommentEnd = "";
        
        print generatePage("template.html");
        return ;
    }
    
    if ($action eq "save")
    {        
        $page = htmlentities($cgi->param("newPageName"));
        $error = saveWikiMarkup($page, $wikiContent);
        
        my $dt = DateTime->now( time_zone => $localTimezone );
        $modificationTime = $dt->dmy("/") . " " . substr($dt->hms(), 0, 5);        
    }    
    
    parseContent();
    if ($action eq "preview")
    {
        $htmlCommentBegin = "";
        $htmlCommentEnd = "";
        $page = htmlentities($cgi->param("newPageName"));        
        my $dt = DateTime->now( time_zone => $localTimezone );
        $modificationTime = $dt->dmy("/") . " " . substr($dt->hms(), 0, 5);            
    }
    else
    {
        $wikiContent = "";
    }    
    print generatePage("template.html");
}

#print "Content-Type: text/html\n\n";
main(@ARGV);
